/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/

package newcertificatesigningrequest;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;

/**
 *
 * @author rmartinezch
 */
public class NewCertificateSigningRequest {
    
    private static PublicKey                    PuKey           = null;
    private static PrivateKey                   PrKey           = null;
    private static KeyPair                      keyPair         = null;
    private static KeyPairGenerator             KeyPairGen      = null;
    private static final String                       SignAlg         = "SHA256WithRSA";
    private static final String                       Alg             = "RSA";
    private static final int                          bits            = 2048;
    // instancia de la clase
    private static NewCertificateSigningRequest NewCSR  = null;
    /**
     * @see 
     * Constructor de la Clase que genera el par de claves RSA
     */
    private NewCertificateSigningRequest(){
        Provider bc = new BouncyCastleProvider();
        Security.insertProviderAt(bc, 1);
        try {
            KeyPairGen  = KeyPairGenerator.getInstance(Alg);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(NewCertificateSigningRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
        KeyPairGen.initialize(bits);
        keyPair = KeyPairGen.generateKeyPair();
        
        PuKey   = keyPair.getPublic();
        PrKey   = keyPair.getPrivate();
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        NewCertificateSigningRequest nCSR = NewCertificateSigningRequest.getInstance();
        PKCS10CertificationRequest csr = null;
        try {
            //        String csr = nCSR.getCSR();
//        System.out.println(csr);
            csr = generatePKCS10();
        } catch (IOException ex) {
            Logger.getLogger(NewCertificateSigningRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            printPEMFormat(csr);
        } catch (IOException ex) {
            Logger.getLogger(NewCertificateSigningRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     *
     * @return única instancia de la clase es retornada
     */
    public static NewCertificateSigningRequest    getInstance(){
        if (NewCSR == null) {
            NewCSR = new NewCertificateSigningRequest();
        }
        return NewCSR;
    }
    
    /**
     *
     * @return retorna la llave publica generada en el constructor
     */
    public PublicKey    getPubKey(){
        return PuKey;
    }
    
    /**
     *
     * @return retorna la llave priavada generada en el contructor
     */
    public PrivateKey   getPriKey(){
        return PrKey;
    }
    
    /**
     *
     * @return retorna el nuevo CSR en el formato PKCS10
     */
    private static PKCS10CertificationRequest generatePKCS10() throws IOException{
        /**
         * Metadata proveniente del registro  de usuario final para la generación del certificado
         */
        X500Principal   subject = new X500Principal("C=PE, "
                + "ST=Lima, "
                + "L=Cercado de Lima, "
                + "O=Reniec, "
                + "OU=Estatal, "
                + "CN=www.reniec.gob.pe, "
                + "EMAILADDRESS=rmartinezch@reniec.gob.pe"

        );
        JcaContentSignerBuilder jcaContentSignerBuilder = new JcaContentSignerBuilder(SignAlg);
        ContentSigner   contentSigner = null;
        try {
            contentSigner = jcaContentSignerBuilder.build(PrKey);
        } catch (OperatorCreationException ex) {
            Logger.getLogger(NewCertificateSigningRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        PKCS10CertificationRequestBuilder builder;
        /**
         * Generación con la metadata suministrada y la clave pública previamente creada 
         */
        builder = new JcaPKCS10CertificationRequestBuilder(subject, PuKey);
        PKCS10CertificationRequest  pKCS10CertificationRequest  = builder.build(contentSigner);
        
        return pKCS10CertificationRequest;
        
    }
    
    /**
     * Expone la función privada para la generación del CSR
     * @return El CSR como String
     */
//    public String getCSR(){
//        byte[] csr = generatePKCS10();
//        return new String(csr);
//    }
    
    /**
     * @param o Objeto en formato PKCS#10
     * @throws IOException
     * @return Impresión de un objeto en el formato de texto Base64
     */
    static void printPEMFormat(Object o) throws IOException{
        OutputStreamWriter output = new OutputStreamWriter(System.out);
        try (PEMWriter pem = new PEMWriter(output)) {
            pem.writeObject(o);
        } catch(IOException ex){
            Logger.getLogger(NewCertificateSigningRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
